/**
 * @file lv_battery.c
 *
 */

/*********************
 *      INCLUDES
 *********************/
#include "lv_battery_private.h"
#include "../../../lvgl/src/core/lv_obj_class_private.h"
#if LV_USE_BATTERY

/*********************
 *      DEFINES
 *********************/

#define MY_CLASS        (&lv_battery_class)

/**********************
 *      TYPEDEFS
 **********************/

/**********************
 *  STATIC PROTOTYPES
 **********************/

static void remove_style(lv_battery_t * battery);
static void add_style(lv_battery_t * battery);
static void lv_battery_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void update_layout(lv_battery_t * battery);
static void update_dync(lv_battery_t * battery);

/**********************
 *  STATIC VARIABLES
 **********************/
const lv_obj_class_t lv_battery_class = {
    .base_class = &lv_obj_class,
    .destructor_cb = lv_battery_destructor,
    .width_def = LV_SIZE_CONTENT,
    .height_def = LV_SIZE_CONTENT,
    .instance_size = sizeof(lv_battery_t),
    .name = "lv_battery",
};

static lv_style_t positive_style;
static lv_style_t main_style;
static uint32_t positive_style_used_count = 0;
static uint32_t main_style_used_count = 0;

static const lv_battery_cfg_t default_cfg = {
    LV_BATTERY_FLASH_NULL | LV_BATTERY_UP_CHARGING | LV_BATTERY_DIS_POSITIVE,/*Dynamic indication of battery status*/
    5,    /*The distance between positive and negative*/    
    0,    /*percentage accuracy,0:0~100,1:0.0~100.0 2:0.00~100.00*/
    NULL, /*battery percentage, NULL is no battery indicator*/
    {20,  40, 5, LV_OPA_50, 0xcccccc, NULL}, /*Description of positive base map*/
    {200 ,80, 5, LV_OPA_50, 0xcccccc, NULL}, /*Description of main body base map*/
    { /*Description of main body pattern filling*/
        /*attribute*/
        3,                /*main border rect size*/
        3,                /*fill in boundary size*/
        LV_OPA_50,        /* Transparency of unfilled areas*/
        LV_OPA_50,        /* Transparency of filled areas*/
        0xffffff,         /* color of unfilled areas*/
        0x00ff00,         /* color of filled areas (when color indication is prohibited)*/
        /*Level color indication*/
        {10, 30, 50, 80, 95},   /*percentage of charging critical point,0-100*/ 
        {5, 10, 30, 50, 80},    /*percentage of discharging critical point,0-100*/ 
        2,                      /*Charging or discharging percentage hysteresis,0-100*/  
        {0xff0000, 0xffc000, 0xd2ff00, 0x00c481, 0x5aff00}, /*charging color indicator,high as much*/ 
        {0xff0000, 0xffc000, 0xd2ff00, 0x00c481, 0x5aff00}, /*discharge color indicator,high as much*/ 
    },
};

/**********************
 *      MACROS
 **********************/

/**********************
 *   GLOBAL FUNCTIONS
 **********************/

lv_obj_t * lv_battery_create(lv_obj_t * parent)
{
    LV_LOG_INFO("begin");
    lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent);
    lv_obj_class_init_obj(obj);
    lv_obj_remove_style_all(obj);
    return obj;
}

void lv_battery_set_cfg(lv_obj_t * obj, const lv_battery_cfg_t *cfg)
{
    LV_ASSERT_OBJ(obj, MY_CLASS);
    lv_battery_t * battery = (lv_battery_t *)obj;
    if(cfg == NULL) cfg = &default_cfg;
    else if(battery->cfg && ((cfg->dync & LV_BATTERY_DIS_POSITIVE) ^ (battery->cfg->dync & LV_BATTERY_DIS_POSITIVE)))
      return;
    
    if(battery->cfg) remove_style(battery);
    battery->cfg = cfg;  
    if(!lv_obj_get_child_count(obj)){
        if(!(cfg->dync & LV_BATTERY_DIS_POSITIVE)){
            lv_obj_t * positive = lv_obj_create(obj);
            lv_obj_remove_style_all(positive);
        }
        lv_obj_t * main = lv_obj_create(obj);
        lv_obj_remove_style_all(main);
        lv_obj_t * rect_area = lv_obj_create(main);
        lv_obj_remove_style_all(rect_area);
        lv_battery_set_cfg(obj, &default_cfg);
    }
    add_style(battery);
    update_layout(battery);
}

void lv_battery_set_dir(lv_obj_t * obj, lv_battery_dir_t dir)
{
    LV_ASSERT_OBJ(obj, MY_CLASS);
    lv_battery_t * battery = (lv_battery_t *)obj;
    if(battery->cfg == NULL) return;
    if(dir == battery->dir) return;
    battery->dir = dir;
    update_layout(battery);
}

void lv_battery_update_percent(lv_obj_t * obj, int32_t pct)
{
    LV_ASSERT_OBJ(obj, MY_CLASS);
    lv_battery_t * battery = (lv_battery_t *)obj;
    if(battery->cfg == NULL) return;
    battery->pct = pct;
    update_dync(battery);
}

const lv_battery_cfg_t *lv_battery_get_cfg(lv_obj_t * obj)
{
    LV_ASSERT_OBJ(obj, MY_CLASS);
    lv_battery_t * battery = (lv_battery_t *)obj;
    return battery->cfg;
}
int8_t lv_battery_get_percent_accuracy(lv_obj_t * obj)
{
    LV_ASSERT_OBJ(obj, MY_CLASS);
    lv_battery_t * battery = (lv_battery_t *)obj;
    if(battery->cfg == NULL) return 0;
    return battery->cfg->pct_accuracy;
}

int32_t lv_battery_get_percent(lv_obj_t * obj)
{
    LV_ASSERT_OBJ(obj, MY_CLASS);
    lv_battery_t * battery = (lv_battery_t *)obj;
    return battery->pct;
}

/**********************
 *   STATIC FUNCTIONS
 **********************/

static void remove_style(lv_battery_t * battery)
{
    const lv_battery_cfg_t *cfg = battery->cfg;
    if(cfg == NULL) return;
    if(cfg->main.style == NULL){
        if(main_style_used_count){
            main_style_used_count--;
            if(main_style_used_count == 0) lv_style_reset(&main_style);
        }
    }

    if(cfg->dync & LV_BATTERY_DIS_POSITIVE) return;
    if(cfg->positive.style == NULL){
        if(positive_style_used_count){
            positive_style_used_count--;
            if(positive_style_used_count == 0) lv_style_reset(&positive_style);
        }
    }

}

static void add_style(lv_battery_t * battery)
{
    const lv_battery_cfg_t *cfg = battery->cfg;
    if(cfg == NULL) return;
    const lv_style_t *style;

    if(cfg->main.style == NULL){
        if(main_style_used_count == 0){
            lv_style_init(&main_style);
            lv_style_set_bg_opa(&main_style, 0);
            lv_style_set_border_width(&main_style, cfg->fill.border_size);
            lv_style_set_border_color(&main_style, lv_color_hex(cfg->main.color));
            lv_style_set_border_opa(&main_style, cfg->main.opa);
            lv_style_set_radius(&main_style,  cfg->main.radius_size);
            lv_style_set_pad_all(&main_style, cfg->fill.pad_size);
        }
        style = &main_style;
        main_style_used_count++;
    }
    else style = cfg->main.style;

    if(!(cfg->dync & LV_BATTERY_DIS_POSITIVE))
        lv_obj_add_style(lv_obj_get_child(&battery->obj, 1), style, 0);  
    else{
        lv_obj_add_style(lv_obj_get_child(&battery->obj, 0), style, 0);
        return;
    }

    if(cfg->positive.style == NULL){ 
        if(positive_style_used_count == 0){
            lv_style_init(&positive_style);
            lv_style_set_bg_color(&positive_style, lv_color_hex(cfg->positive.color));
            lv_style_set_bg_opa(&positive_style, cfg->positive.opa);
            lv_style_set_radius(&positive_style, cfg->positive.radius_size);
        }
        style = &positive_style;
        positive_style_used_count++;
    }
    else style = cfg->positive.style;
    lv_obj_add_style(lv_obj_get_child(&battery->obj, 0), style, 0);


}

static void lv_battery_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
  LV_UNUSED(class_p);
  lv_battery_t * battery = (lv_battery_t *)obj;
  remove_style(battery);
}

static const lv_align_t dir_to_align[] = {
    LV_ALIGN_LEFT_MID, LV_ALIGN_RIGHT_MID, 
    LV_ALIGN_TOP_MID,  LV_ALIGN_BOTTOM_MID };

static const lv_align_t dir_to_align_out[] = {
    LV_ALIGN_OUT_RIGHT_MID,  LV_ALIGN_OUT_LEFT_MID, 
    LV_ALIGN_OUT_BOTTOM_MID, LV_ALIGN_OUT_TOP_MID };

static void update_layout(lv_battery_t * battery)
{
    lv_obj_t * obj = &battery->obj;
    const lv_battery_cfg_t *cfg = battery->cfg;
    /*update positive*/
    lv_obj_t * positive; 
    lv_obj_t * main;
    if(!(cfg->dync & LV_BATTERY_DIS_POSITIVE)){
        positive = lv_obj_get_child(obj, 0);
        main = lv_obj_get_child(obj, 1);
        if(battery->dir < 2) 
          lv_obj_set_size(positive, cfg->positive.len, cfg->positive.diam);
        else lv_obj_set_size(positive, cfg->positive.diam, cfg->positive.len);
        lv_obj_set_align(positive, dir_to_align[battery->dir]);
    }
    else{
      positive = NULL;
      main = lv_obj_get_child(obj, 0);
    } 
    /*update main*/
    if(positive){
        lv_align_t align_out = dir_to_align_out[battery->dir];
        uint32_t space =  cfg->space_positive_main;
        if(battery->dir & 0x01) space = - space;     
        if(battery->dir < 2){
            lv_obj_set_size(main, cfg->main.len, cfg->main.diam);
            lv_obj_align_to(main, positive, align_out, space, 0);
        }
        else{
            lv_obj_set_size(main, cfg->main.diam, cfg->main.len);
            lv_obj_align_to(main, positive, align_out, 0, space);
        }
    }
    else{
        if(battery->dir < 2) 
          lv_obj_set_size(main, cfg->main.len, cfg->main.diam);
        else lv_obj_set_size(main, cfg->main.diam, cfg->main.len);
        lv_obj_set_align(main, dir_to_align[battery->dir]);
    }
    /*Update the dynamic part in the main body*/
    lv_obj_t * dync_area = lv_obj_get_child(main, 0);
    lv_obj_set_size(dync_area, LV_PCT(100), LV_PCT(100));
    lv_obj_set_style_bg_opa(dync_area, cfg->fill.null_opa, 0);
}

static void update_dync(lv_battery_t * battery)
{
    lv_obj_t * obj = &battery->obj;
    const lv_battery_cfg_t *cfg = battery->cfg;   
    lv_obj_t * main;
    if(cfg->dync & LV_BATTERY_DIS_POSITIVE) main = lv_obj_get_child(obj, 0);
    else main = lv_obj_get_child(obj, 1);
    lv_obj_t * dync_area = lv_obj_get_child(main, 0);

    lv_obj_set_style_bg_color(dync_area, lv_color_hex(cfg->fill.full_color), 0);
}

#endif /*LV_USE_BATTERY*/
